home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-05-07 | 32.8 KB | 1,275 lines |
-
-
- public _stdout
-
-
- ; Simple Sound Engine ⌐ Copyright 1990 Software Alchemy All Rights Reserved
- ; Revision 0.70 Monday January 8 1990 Anselm Hook
- ; Revision 0.71 Jan 29 90
- ; Revision 0.80 Feb 2 90
-
- ; Audio can be crunched by discarding the lowest 4 bits of data, and then
- ; overlapping the second half of the data with the first, although data is
- ; lost by this technique it still sounds pretty good. I didn't do this here.
-
- ;
- ; There are 2 bugs:
- ;
- ; 1) I am *still* losing serial data...
- ; My current hypothesis is that the InternalSYNC routine is
- ; breaking up packets despite theory to the contrary.
- ; There is no fix for this except having a proper externally
- ; supplied SYNC, if I do detect external SYNC I do turn mine off,
- ; in which case it should work properly, although my keyboard
- ; doesn't supply a sync to me.
- ; [I am using a 256 byte circular buffer with raw serial input.]
- ; search for the flag 'AZUREVIDIAN' to turn internal sync on.
- ;
- ; 2) Sounds do not get turned off quickly enough in StopSound and
- ; as a consequence more channels are in use at any moment than
- ; required, this gives a clipping effect to playback. This doesn't
- ; happen in the sequencer, but only in the playback routine.
- ;
- ; Another problem is that the playback runs at level 6, which means
- ; sound-interupts may not happen as quickly as possible. I know
- ; this causes a problem because when I had the level 6 just set a
- ; flag, and ran the playback from non-interupt mode, it was flawless.
- ; [Ie: sound-interupts also help stop sounds and free up channels].
- ;
- ; You will notice the effect in the game music, it sounds like it
- ; goes into solo mode for small sequences.
-
-
- ; Note I spell Interupt with one 'R', I forward this motion.
-
- ;
- ; 64 Instruments currently, 16 Midi Channels (ie: 16 noises at any time-slice)
- ; NOTE: Pass sound # to play in D0, must be multiple of 16.
- ; The MusicSequencer relys on an external MIDI SYNC.
- ; But the MusicEngine, which plays your finished product does its own timing.
- ;
-
- ;
- ; I think the following two features would be nice to have,
- ; although I am not going to program them myself:
- ;
- ; #1)
- ; overlay mode erases any notes colliding with same channel at same sync time
- ; overlay mode is good for final touch ups, and for multiple keyboards.
- ; An entire music score could be built with one pass of the sequencer.
- ; #2)
- ; erase mode erases any notes on same channel right to end of sequence
- ; erase mode is good for a single keyboard enviroment where multiple
- ; passes of the sequencer (on diff tracks) are used to build a music score.
- ;
-
- blanks on ; Bezerk Aztek declarations
-
- MIDI_NULL equ $00 ; Supported Midi Commands
- MIDI_NOTE equ $90
- MIDI_NOTEOFF equ $A0
- MIDI_ENDSONG equ $FC
- MIDI_SYNC equ $F8
-
- MIDI_FORCERTS equ $ef ; Internal Commands
-
- MIDI_NOTECHANGE equ $B0 ; Unsupported Commands
- MIDI_CHANGESAM equ $C0
- MIDI_SONGSTART equ $FA
-
-
-
- MAXSAM equ 64 ; # of Instruments Maximum Allowed...
- BUFFERSIZE equ $20000 ; Memory of Music Score
-
-
- public SoundEngineStart ; Warm up Audio Engine
- public SoundEngineStop ; Stop Audio Engine
- public SoundPlay ; Play Requested Sound D0>>4
- public SoundStop ; Stop Requested Sound D0>>4
-
- public SoundGetHunk
- public SoundDecodeHunk
- public SoundReleaseHunk
-
- public MusicEngineStart
- public MusicEngineStop
- public MusicEnginePause
- public MusicEngineUnPause
-
- public MusicSequencer
-
- public JustPlayIt
- public StopTheMusic
-
- public Cases ;(use for debugging "optimizations" )
- public MCases
-
-
- ;**************************************************************************
- ; stand alone module, just compile and link to itself only, and run.
- StandAlone
- clr.l d0
- Borrow lea skipme(pc),a0 ;(flag, sequencer or player)
- move.l d0,(a0)
-
- lea one(pc),a0
- bsr dumpmess
- move.l #BUFFERSIZE,d0
- bsr MusicMem ; Get and Prep Mem
- beq.s StandX3
-
- lea two(pc),a0
- bsr dumpmess
- lea FileName(pc),a0
- bsr SoundGetHunk ; A0 = Pointer to File
- beq.s StandX2
- lea fh(pc),a1
- move.l a0,(a1)
-
- move.l a0,-(a7)
- lea three(pc),a0
- bsr dumpmess
- move.l (a7)+,a0
- bsr SoundDecodeHunk ; Decode from A0
- beq.s StandX
-
- lea seven(pc),a0 ; say LOAD SCORE
- bsr dumpmess
-
- lea FileName2(pc),a0
- move.l a0,d1
- bsr FindFileSize ; d0 = size or zero
- beq.s noload
- move.l MusicBuffer(pc),a0
- add.l d0,a0
- cmp.l MusicBufferEnd(pc),a0 ; too big?
- bcc.s noload
- lea FileName2(pc),a1
- move.l a1,d1 ; D1 = Name
- move.l MusicBuffer(pc),a3 ; A3 = Dest
- move.l d0,a2 ; A2 = Size
- move.l d0,a5 ; (save size)
- bsr LoadFile
- beq.s noload
- move.l MusicBuffer(pc),a3 ; Advance to end of load
- add.l a5,a3
- move.b #MIDI_ENDSONG,(a3)+ ; Make sure we have a stop
- move.b #MIDI_ENDSONG,(a3)+ ; make sure stop is visible
- move.b #MIDI_ENDSONG,(a3)+
- lea BufferMerge(pc),a1 ; (dummy, flag merge)
- move.l a3,(a1)
- lea four(pc),a0 ; Say loaded
- bsr dumpmess
- bra.s load
- noload move.l MusicBuffer(pc),a3
-
- load lea five(pc),a0
- bsr dumpmess
-
- move.l skipme(pc),d0
- bne JustPlayItCont
- ; (A3 = End of Song IN & OUT)
-
-
- btst #7,$bfe001
- bne.s 1$
- ;----------------------------------------------------------------------------
- lea didstartit(pc),a0 ; hack
- move.b #1,(a0)
- bsr SoundEngineStart ; (playback only mode)
- move.l MusicBuffer(pc),a0
- bsr MusicEngineStart
- moveq #24,d0 ; Speed!
- bsr MusicEngineUnPause
- 0$ btst #6,$bfe001
- bne.s 0$
- bsr MusicEngineStop
- bsr SoundEngineStop
- bra.s StandX
- ;----------------------------------------------------------------------------
- 1$
-
-
- ;----------------------------------------------------------------------------
- bsr MusicSequencer ; Play and allow sequencing
- ;----------------------------------------------------------------------------
- ; (A3 = End of Song IN & OUT)
- ; SAVE SCORE
- sub.l MusicBuffer(pc),a3
- cmp.l #20,a3 ; Don't save if very small
- bcs.s StandX
- cmp.l #BUFFERSIZE-3,a3 ; bug???
- bcc.s StandX
- move.b #MIDI_ENDSONG,(a3)+ ; Make sure we have a stop
- move.b #MIDI_ENDSONG,(a3)+ ; make sure stop is visible
- move.b #MIDI_ENDSONG,(a3)+
- move.l a3,a2 ; a2 = size
- move.l MusicBuffer(pc),a3 ; a3 = source
- lea FileName2(pc),a0
- move.l a0,d1 ; D1 = Name
- bsr SaveFile
- lea six(pc),a0
- bsr dumpmess
-
- StandX move.l fh(pc),a1
- bsr SoundReleaseHunk
- StandX2 move.l MusicBuffer(pc),a1
- move.l #BUFFERSIZE,d0
- bsr FreeMemory
- StandX3 moveq #0,d0
- rts
-
- ;----------------------------------------------------------------------------
- JustPlayIt
- bsr SoundEngineStart ; (always start audio at least)
- moveq #-1,d0
- bra Borrow ; (either comes here or err)
- JustPlayItCont
- lea didstartit(pc),a0
- move.b #1,(a0)
- move.l MusicBuffer(pc),a0
- bra MusicEngineStart ; Just play song (interupts)
- StopTheMusic
- bsr SoundEngineStop
- move.b didstartit(pc),d0 ; (allow game without music)
- beq.s nope
- bsr MusicEngineStop ; stop interupts
- bra StandX
- nope rts
- didstartit:
- dc.l 0
- ;----------------------------------------------------------------------------
-
-
- FileName dc.b 'music.hunk',0,0
- even
- FileName2 dc.b 'music.score',0,0
- even
- fh dc.b 'ANDY HOOK '
- skipme dc.l 0
-
- ;**************************************************************************
-
- SoundEngineStart
- move.l #$dff000,a6 ; A6 = Base of Hardware
- lea SoundVector(pc),a0
- move.l $70,(a0) ; (save)
- bsr SoundEngineStop ; (stop any previous activity)
- lea SoundInterupt(pc),a0 ; A0 = My Audio Handler
- move.l a0,$70 ; Audio Interupt Level 4
- move.w #$c780,$9a(a6) ; Enable Audio Interupts
- rts
-
- SoundEngineStop
- move.l #$dff000,a6 ; A6 = Base of Hardware
- move.w #$000f,$96(a6) ; Turn OFF All Audio DMA
- move.w #$0780,$9c(a6) ; Clear Audio Interupts Pend
- move.w #$0780,$9a(a6) ; Disable Audio Interupts
- move.l SoundVector(pc),$70 ; (restore)
- bchg #1,$bfe001 ; Audio Filter On/Off
- rts
-
- dc.b "As we become less like those we know, we "
- dc.b "seem to become more like those we hear of"
-
- ;
- ; Sound Interupts occur only when its time to kill a sound
- ;
- SoundInterupt
- move.w #$2780,$dff09a ; Prevent interupt collisions
- movem.l d0/d1/a0/a6,-(a7)
- lea $dff000,a6
- move.w $1e(a6),d0 ; Interupt Request Read
- and.w #$0780,d0
- moveq #0,d1
- move.w d0,d1 ; (save)
- lsr.w #7,d1
- move.w d1,$96(a6) ; Stop these Audio DMA
- bsr SoundPause
- move.w d0,$9c(a6) ; Clear these Audio Pending
-
- or.l #$01000010,d1 ; Frequency Fast, Volume Low
- lea SoundChannel(pc),a0
- btst #0,d1
- beq.s 1$
- clr.b (a0)
- clr.b 4(a0)
- move.l d1,$a6(a6)
- 1$ btst #1,d1
- beq.s 2$
- clr.b 1(a0)
- clr.b 5(a0)
- move.l d1,$b6(a6)
- 2$ btst #2,d1
- beq.s 3$
- clr.b 2(a0)
- clr.b 6(a0)
- move.l d1,$c6(a6)
- 3$ btst #3,d1
- beq.s 4$
- clr.b 3(a0)
- clr.b 7(a0)
- move.l d1,$d6(a6)
- 4$
- ;eor.w #$8780,d0 (optional)
- ;move.w d0,$9a(a6)
- move.w #$a780,$dff09a
- movem.l (a7)+,d0/d1/a0/a6
- rte
-
- ;
- ; Pointer to Audio Data
- ;
- SoundRaw dc.l 0
- SoundVector dc.l 0
-
-
- ;
- ; User List of available Sounds
- ; Format
- ; Delta-Start.l, Length, Frequency, Volume, Priority, 0,0,0
- ;
- SoundList dcb.w 16*MAXSAM,0
-
- ;
- ; Sound Channel Arbitration Manager
- ; x = Active Priority, 0 = Available
- ;
- SoundChannel dc.b 0,0,0,0
- SoundOwner dc.b 0,0,0,0
- SoundExtra dc.b 0,0,0,0 ;<- Extra Info for Note-OFF
- dc.b 0,0,0,0
- ;
- ; Arbitration system finds best channel to free based on priority
- ; of sound request. Sounds of the same priority may overrun each other.
- ; D0 = Requested Sound to Play
- ; D7 = Off extra-information tag (optional)
- SoundPlay
- move.w #$2000,$dff09a ; Allow Game Audio Also!!!
- lea SoundList(pc),a0
- lea (a0,d0.w),a0
- move.l 6(a0),d5 ; Pre-get Frequency, Volume
- moveq #0,d7 ; (off tag defaults to zero)
- bsr SoundPlayTwo
- move.w #$a000,$dff09a
- rts
-
- SoundPlayTwo
- tst.b d5 ; Is Really Note Off?
- beq SoundStop
- lea SoundList(pc),a0
- lea (a0,d0.w),a0 ; A0 = Pointer to Sound Info
- move.w 10(a0),d2 ; D2 = Request Priority
- moveq #3,d4 ; D4 = Channel # , Loop
- moveq #99,d3
- 1$ move.b SoundChannel(pc,d4.w),d1 ; D1 = Channel Priority
- beq.s 3$ ; Available Sound Channel?
- cmp.b d1,d2
- bcs.s 2$
- ; cmp.b d2,d1
- ; bcc.s 2$
- move.w d4,d3 ; D3 = Last Available Priority
- 2$ dbf d4,1$
- cmp.b #99,d3
- beq.s SoundPlay900 ; No Low Priority Slots Open
- move.w d3,d4
- 3$
- ; D4 = Channel #0-3
- lea SoundChannel(pc),a1
- move.b d2,(a1,d4.w) ; D2 = New Sound Priority
- move.b d0,4(a1,d4.w) ; D0 = New Sound BackPtr
- move.b d7,8(a1,d4.w) ; D7 = Optional Off Tag
- swap d7
- move.b d7,12(a1,d4.w)
- swap d7
-
- moveq #1,d0
- lsl.w d4,d0 ; D0 = Hardware Int # 1-2-4-8
- move.w d0,d1
- lsl.w #7,d1 ; D1 = (0-8)*128=Hardware DMA
- ; D0 = %0000000000001234
- ; D1 = %0000012340000000
-
- move.l #$dff000,a6 ; A6 = Base of Hardware
- move.w d0,$96(a6) ; Turn off Channel DMA
- bsr SoundPause ; Delay against audio clicking
- move.w d1,$9a(a6) ; Disable Int
-
- lea $dff0a0,a2 ; A2 = Audio Hardware
- lsl.w #4,d4 ; 0-3 * 16
- add.w d4,a2
-
- ;move.l SoundRaw(pc),a1 ; A1 = Base of Sound Data
- ;add.l (a0),a1
- move.l (a0),a1 ; (jan 29 90, offsets are precomputed now)
-
- move.l a1,(a2) ; Memory
- move.w 4(a0),4(a2) ; Length
- move.l d5,6(a2) ; Frequency, Volume
- bsr SoundPause
-
- or.w #$8000,d0
- move.w d0,$96(a6) ; Re-enable DMA
- bsr SoundPause ; (wait for started)
- ; 4(a2) Repeat can go here
-
- move.w d1,$9c(a6) ; Clear Started-Sound Flag
- or.w #$8000,d1
- move.w d1,$9a(a6) ; Re-enable Interupt
- SoundPlay900
- rts
-
- SoundPause
- move.l d1,-(a7) ; Time Delay to prevent clicks
- move.w #320,d1
- 1$ tst.b d1
- dbf d1,1$
- move.l (a7)+,d1
- rts
-
-
-
- ;
- ; Stop a requested sound, scans active sounds and stops if active
- ; D0 = sound to stop
- ; D7 = optional sentry/signature/tag/ID for more accurate off
- SoundStop
- movem.l d0-d3/a0/a6,-(a7)
- lea $dff000,a6
- ; D0 = Sound to stop
- lea SoundOwner(pc),a0
- moveq #3,d1 ; D1 = Loop Control
- 1$ cmp.b (a0,d1.w),d0 ; D0 = Found Active Sound?
- bne.s 2$
- cmp.b 4(a0,d1.w),d7 ; Tags Match?
- bne.s 2$
- move.l d7,d2
- swap d2
- cmp.b 8(a0,d1.w),d2
- bne.s 2$
-
- clr.b -4(a0,d1.w)
- clr.b 0(a0,d1.w)
-
- move.l d1,d2
- lsl.b #4,d2 ; D2 = Channel Hardware
- lea $dff0a0,a1 ; A1 = Audio Hardware Base
- move.w #1,8(a1,d2.w) ; Channel Volume (0 makes pop)
- move.w #100,6(a1,d2.w) ; Period (0 makes click)
-
- moveq #1,d2
- lsl.b d1,d2 ; D2 = Channel Bit #
- move.w d2,$96(a6) ; Stop Audio
- bsr SoundPause
- lsl.w #7,d2
- move.w d2,$9c(a6) ; Clear Interupt Pending
-
- 2$ dbf d1,1$
- movem.l (a7)+,d0-d3/a0/a6
- nyet: rts
-
- ;***************************************************************************
- ;***************************************************************************
- ; Music Engine written entirely in The Transform Language ⌐ 1990 Anselm Hook
- ; The Music Sequencer was tested using a YAMAHA SHS-10R Midi Synth Keyboard.
-
- ;
- ; For those of you who are already freaked out by all the numbers:
- ;
- ; We had a bunch of hardware level stuff to setup the SoundEngine Interupts,
- ; Now we are going to setup a bunch more interupts for the MusicEngine so
- ; that it can keep track of time properly. Aside from that the only other
- ; hardware stuff is the direct SERIAL I/O code....
- ;
-
- ;
- ; Simply load the music file up and pass start in a0
- ; end must be terminated with a $fc
- ;
- MusicEngineStart
- ; a0 = the data!
- lea musicstart(pc),a1
- move.l a0,(a1)
- lea musicpos(pc),a1
- move.l a0,(a1)
- rts
-
- MusicEngineUnPause
- move.b didstartit(pc),d1
- beq nyet
- lea musicpos(pc),a0 ; Start/Restart Music
- move.l musicstart(pc),(a0)
- lea MusicServer(pc),a0
- bra TimerStartSpeed
-
- MusicEngineStop
- MusicEnginePause
- move.b didstartit(pc),d1
- beq nyet
- bra TimerStop
-
-
- ;
- ; MusicServer, an interupt driven routine which plays your music.
- ;
- MusicServer
- btst #1,$bfdd00 ; Clear INT_FLAG + Test Case
- beq.s MusicServer100
- movem.l d0-d7/a0-a6,-(a7)
- bsr MusicServerTwo
- movem.l (a7)+,d0-d7/a0-a6
- MusicServer100
- move.w #$2000,$dff09c ; Clear interupt
- rte
-
- MusicServerTwo
- lea musicpos(pc),a0
- move.l (a0),a3
- Music3 move.b (a3)+,d0
- Music4 move.b d0,d1
- and.w #15,d1
- lsr.b #3,d0 ; 0-15 * 2
- and.w #30,d0
- jmp Cases(pc,d0.w)
- Cases bra.s Music3 ; #0
- bra.s Music3 ; #1
- bra.s Music3 ; #2
- bra.s Music3 ; #3
- bra.s Music3 ; #4
- bra.s Music3 ; #5
- bra.s Music3 ; #6
- bra.s Music3 ; #7
- bra.s Music3 ; #8
- bra.s Note ; #9
- bra.s Music3 ; #10
- bra.s Music3 ; #11
- bra.s Music3 ; #12
- bra.s Music3 ; #13
- rts ; #14 FORCE RTS
- Sync cmp.b #12,d1 ; #15 END?
- bne.s 1$
- move.l musicstart(pc),a3
- 1$ lea musicpos(pc),a0
- move.l a3,(a0)
- rts
-
- ;---------------------------------------------------------------------------
- periods dc.w 856,808,760,720,680,640,604,572,540,508,480,452,428,404,380,360
- dc.w 340,320,302,286,270,254,240,226,214,202,190,180,170,160,151,143
- dc.w 135,127,120,113,107,101
- ;---------------------------------------------------------------------------
-
- Note add.b d1,d1 ; d1 = midi channel 0-15
- move.w instr(pc,d1.w),d6 ; D6 = Instr of Channel 0-15
- move.b -1(a3),d7 ; Signature for Accurate OFF
- NoteTwo move.b (a3)+,d0 ; D0 = Note #0-x
- cmp.b #$35+64,d0
- bcc.s Music4
- sub.b #$35,d0 ; Yamaha Lowest Key #
- swap d7
- move.b d0,d7 ; construct signature id.w
- swap d7 ; made of command,channel,note
-
- add.b d0,d0
- move.w periods(pc,d0.w),d5 ; D5 =Note=Rate(in upper word)
- swap d5
- move.b (a3)+,d5 ; D5 = Vol %xxxxxxxxxx111111
- lsr.b #1,d5 ; music volume to half, yamaha only!!!
- move.w d6,d0
-
- bsr SoundPlayTwo ; Don't corrupt d6/a3-a7
- bra.s NoteTwo ; Any more events in packet?
-
- ;---------------------------------------------------------------------------
- instr dc.w 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224,240
- ;dc.w 256,17*16,18*16,19*16,20*16,21*16,22*16,23*16,24*16,25*16
- ;dc.w 26*16,27*16,28*16,29*16,30*16,31*16
- ;---------------------------------------------------------------------------
-
- NoteChange
- add.b d1,d1 ; Channel 0-15
- move.b (a3)+,d0 ; Instr # 0-x
- lsl #4,d0 ; *16 to for SoundPlay format
- lea instr(pc),a0
- move.b d0,(a0,d1.w)
- bra.s Music3
-
- musicpos dc.l 0
- musicstart dc.l 0
-
- ;************************************************************************
- ;************************************************************************
- ; Utilities for use with music engine
-
- ;
- ; Music sequencer:
- ; RMB - Restart Music - Will merge your music with previous score.
- ; LMB - Exit, saves music to disk
- ;
-
-
- ;Sequencer theory:
- ;
- ;Sequencer - Switch thru midi-events
- ; (get-events)
- ; note-on
- ; play, store
- ; pulse
- ; read bufferA till pulse
- ; play,store (overlay/erase mode)
- ; write pulse
- ;
- ;play - call music player manually.
- ;store - write bufferB
- ;
-
-
- ;************************************************************************
- MusicSequencer
- bsr MusicRestart
- bsr SerialStart
- bsr SoundEngineStart
- lea InternalSync(pc),a0
- bsr TimerStart
- bsr MusicIn
- bsr TimerStop
- bsr SoundEngineStop
- bra SerialStop
- ;************************************************************************
-
-
- ;************************************************************************
- ;
- ; Music In Store, main routine
- ; Watches for user restart/quit input
- ; Reads raw-serial data, plays and stores it (till sync)
- ; Upon sync, merges in old data
- ; adds sync and loops...
- ;
- Unknown
- MusicIn
- btst #6,$bfe001 ; LMB
- bne.s 1$
- rts
- 1$ btst #7,$bfe001 ; RMB (#6 is port #2 bytheway)
- bne.s 2$ ; (remember to refresh sensors)
- 0$ move.w #$ff0,$dff180 ; YELLOW
- btst #7,$bfe001 ; (must release also)
- beq.s 0$
- bsr MusicRestart
- 2$ bsr MidiIn ; Wait for Midi Event
- MusIn2 move.b d0,d1 ; D1 = Channel + Command
- lsr.b #3,d0 ; D0 = Command
- and.w #30,d0
- jmp MCases(pc,d0.w)
- MCases bra.s Unknown
- bra.s Unknown
- bra.s Unknown
- bra.s Unknown
- bra.s Unknown
- bra.s Unknown
- bra.s Unknown
- bra.s Unknown
- bra.s Unknown
- bra.s MNote ; #9
- bra.s Unknown ; #10
- bra.s Unknown ; #11
- bra.s Unknown ; #12
- bra.s Unknown ; #13
- bra.s Unknown ; #14
- bra MSync ; #15
- ;
- ; Store and play user input
- ;
- MNote cmp.l MusicBufferEnd(pc),a3
- bcc.s MEnd
-
- ; within this sync I can receive any other packet
- ; or more of these (notes)
- ; if I do not recognize the packet I should pass it back to the
- ; main routine for it to determine the packet type.
-
- bsr MidiIn ; D0 = Note #
- cmp.b #$35,d0 ; Not my packet
- bcs.s MusIn2
- cmp.b #$35+64,d0 ; Not my packet
- bcc.s MusIn2
- move.b d0,d2
- bsr MidiIn ; D0 = Volume
- cmp.b #$41,d0 ; Transmission bug???
- bcc.s MusIn2
-
- tst.b d1 ; Command+Channel (once only)
- beq.s 1$
- move.b d1,d7 ; D7 = Off Signature, dont hurt me!
- move.b d1,(a3)+
- and.w #15,d1
- add.b d1,d1
- lea instr(pc),a0
- move.w (a0,d1.w),d6 ; D6 = Instrument # (dont damage)
- 1$ clr.b d1
-
- ; whenever we play a note it goes to one of the four channels, now we are
- ; going to pass a note off for that note, but we may inadvertantly be turning
- ; off another note which happens to be using that channel dma. To actually
- ; turn off the proper note we must be able to positively identify that
- ; particular note and midi source channel.
-
- ; so remembering a sound involves remembering its host frequency and channel.
- ;
- ; whenever we do a midi-change to a specific channel we must clear all note
- ; offs for that channel; so that a note off for an old instrument doesn't
- ; affect any new ones.
- ;
- move.b d2,0(a3) ; Note #0-x
- move.b d0,1(a3) ; Volume
- move.b #MIDI_FORCERTS,2(a3) ; (return to me)
- move.w d1,-(a7)
- bsr NoteTwo ; A3+=3, PASS D6!!!
- move.w (a7)+,d1
- subq.w #1,a3 ; Delete FORCE_RTS
- bra.s MNote
-
- MEnd move.w #$f00,$dff180
- bra MusicIn
-
-
- ;
- ; We've played user input, now merge in old score (of this sync).
- ;
- ; We install a fake handler and pass old data as fresh input
- ; The full capacity of the MusicSequencer can be applied to old data now.
- ; The only thing we do is catch the old sync and abort at that time.
- ; (Thus the old data timing is run off new external syncs also)
- ;
- MSync move.w #$ff01,$dff034 ; Hardware Refresh for RMB
- cmp.b #$f8,d1
- bne Unknown
- cmp.l MusicBufferEnd(pc),a3
- bcc.s MEnd
- lea ReadFn(pc),a0 ; On pass #2?
- lea Sinking(pc),a1
- cmp.l (a0),a1
- beq.s MSync2
- move.l (a0),4(a0) ; Save Old Fn
- move.l a1,(a0)
- bra MusicIn ; Eat Fake Events till SYNC
- MSync2 move.l 4(a0),(a0)
- move.b #MIDI_SYNC,(a3)+
- bra MusicIn
- Sinking move.b #MIDI_SYNC,d0
- cmp.l a3,a4 ; New Overran Old ???
- bcs.s SyncHit
- cmp.l MusicBufferEnd(pc),a4 ; Read Hit end
- bcc.s SyncHit
- move.b (a4)+,d0 ; Read Old Music
- SyncHit rts
-
- MidiIn move.l ReadFn(pc),a0
- jmp (a0)
- ;***********************************************************************
-
- dc.b "When you find yourself between "
- dc.b "the Devil and the deep blue sea "
-
- MusicRestart
- lea ReadFn(pc),a0
- lea SerialInWait(pc),a1
- move.l a1,(a0)
-
- move.l BufferMerge(pc),a0 ; First Time?
- cmp.l #0,a0
- beq.s UseOld
-
- ; a single buffer has the source being copied back to the dest.
- ; the trick is that the source must start suitably far away from the dest.
- ; as a matter of fact the best possible place for the source to be is as
- ; close to the very end as possible, so overwrite happens last if possible.
-
- move.l MusicBuffer(pc),a1
- move.l MusicBufferEnd(pc),a0
- cmp.l a3,a1
- beq.s 2$
- 1$ move.b -(a3),-(a0) ; Copy up to end...
- cmp.l a3,a1
- bcs.s 1$ ; A3 will equal *MusicBuffer
- 2$ move.l a0,a4 ; A4 = SOURCE, A3 = DEST
- 4$ cmp.l a1,a0
- bcc.s 3$
- move.b #MIDI_SYNC,-(a0) ; (Erase Rest of Buffer)
- bne.s 4$
- 3$ bra.s Neateh
-
- UseOld move.l MusicBufferEnd(pc),a4 ; A4 = Tracks SOURCE (default)
- Neateh move.l MusicBuffer(pc),a3 ; A3 = Tracks DEST
- lea BufferMerge(pc),a0
- move.l a4,(a0)
- rts
-
-
- MusicMem
- move.l d0,a2
- bsr AllocateMemory
- beq.s MusicMemFail
- move.l d0,d1
- add.l a2,d1
- lea MusicBuffer(pc),a0
- move.l d0,(a0)+ ; (start/end)
- sub.l #10,d1 ; for checking if *near* end
- move.l d1,(a0)
- move.l d0,a0
- move.b #MIDI_SYNC,d0 ; Prepare recording buffer
- 1$ move.b d0,(a0)+
- cmp.l a0,d1
- bne.s 1$
- moveq #1,d0
- MusicMemFail
- rts
-
- ReadFn dc.l 0,0
- MusicBuffer dc.l 0 ; Start both buffers
- MusicBufferEnd dc.l 0 ; End of both buffers
- BufferMerge dc.l 0 ; Start of Source buffer
-
- ;****************************************************************************
- ;****************************************************************************
-
- ;some serial in stuff i don't set:
- ;move.w #115,$dff032 ; BAUD = 31250
- ;bclr #6,$bfde00 ; CIAB, SPMODE=Input
- ;move.b #3,$bfd200 ; MODE = Input
- ;move.b #$ff,$bfd000 ; Turn on everything...
-
- SerialOut
- move.w #115,$dff032 ; BAUD = 31250
- move.b #$c0,$bfd200 ; MODE = Output
- move.b #0,$bfd000
- 1$ btst #13-8,$dff018 ; Buffer Ready?
- beq.s 1$
- move.w d0,$dff030 ; Output new byte
- rts
-
- SerialInWait
- move.w d1,-(a7)
- lea SerialPos(pc),a0
-
- move.w (a0),d1 ; My Read Pos
- SWait move.b 3(a0),d0 ; Sync Timer Interupt Pos
- cmp.b d1,d0 ; Caught up, wait for more
- beq.s AddSync
-
- move.b 4(a0,d1.w),d0 ; D0 = Value to Return
-
- cmp.b #MIDI_SYNC,d0 ; External SYNC found!!!
- bne.s 1$
- move.w #80,-4(a0) ; Disable internal SYNCing
- 1$ tst.w -4(a0)
- beq.s 2$
- subq.w #1,-4(a0) ; Try to enable...
-
- 2$ addq.b #1,d1 ; Add molulo 255
- move.b d1,1(a0) ; Store modulo 255
- move.w (a7)+,d1
- rts
-
-
- AddSync
- bra.s SWait ; AZUREVIDIAN (remove me)
- tst.w -4(a0) ; Externally overriden?
- bne.s SWait
- tst.w -2(a0)
- beq.s SWait
- 4$ subq.w #1,-2(a0)
- move.b #MIDI_SYNC,d0
- move.w (a7)+,d1
- rts
-
- ;
- ; the theory for internal syncs is that the midi-device should dump data
- ; in packets, such that serialwait should never wait for the rest of a packet
- ; If this is true then serialwait only waits during interm periods between
- ; packets. This is a safe time to issue a sync without breaking packets.
- ;
- ; In reality I get so many syncs that they just about override everything
- ; else.
- ;
-
- SerialInterupt
- btst #11-8,$dff01c ; My Interupt?
- beq.s 2$
-
- movem.l d0/d1/a0,-(a7)
- move.w $dff018,d0 ; Read Byte from Hardware
- lea SerialPos(pc),a0 ; Circular buffer
- move.w 2(a0),d1 ; Get Buffer Pointer, clean.w
- move.b d0,4(a0,d1.w) ; Store data byte to buffer
- addq.b #1,d1 ; Advance pointer Modulo 255
- move.b d1,3(a0) ; store pointer Modulo 255
- movem.l (a7)+,d0/d1/a0
-
- 2$ move.w #$0800,$dff09c ; Clear INTF_RBF in INTREQ
- rte
- dc.w 0 ; Sync Disable Time Flag
- dc.w 0 ; Sync Flag
- SerialPos dc.w 0,0 ; Ptr #2,#3
- dcb.l 256/4,0 ; Serial Buffer
-
- ;****************************************************************************
-
- SerialStart
- move.w #$0800,$dff09a
- move.w #115,$dff032 ; BAUD = 31250
- lea SerialVector(pc),a0
- move.l $74,(a0) ; Save Old Vector
- lea SerialInterupt(pc),a0
- move.l a0,$74
- move.w #$0800,$dff09c ; Clear any previous
- move.w #$c800,$dff09a ; Turn on Serial Interupt
- rts
-
- SerialStop
- move.w #$0800,$dff09a
- lea SerialVector(pc),a0
- move.l (a0),$74
- rts
- SerialVector dc.l 0
-
- ;****************************************************************************
-
- InternalSync
- btst #1,$bfdd00 ; Mine?
- beq.s 0$
-
- move.l a0,-(a7)
- lea SerialPos(pc),a0
- addq.w #1,-2(a0) ; One More Sync!!!
- move.l (a7)+,a0
-
- 0$ move.w #$2000,$dff09c
- rte
-
- ;************************************************************************
- ;
- ; A0 = Handler function, must turn on yourself (poke $dff09a,$8000+$2000)
- ;
- TimerStart
- moveq #24,d0 ; Speed!
- TimerStartSpeed
- move.w #$2000,$dff09a ; Disable external interupt
- lea TimerVector(pc),a1
- move.l $78,(a1)
- move.l a0,$78 ; Set up new interupt server
- lea $bfd000,a1
- move.b #$7f,$d00(a1) ; Disable CIAB interupt
- move.b #204,$400(a1) ; TimerA for 1 millisecond
- move.b #2,$500(a1)
- move.b d0,$600(a1) ; TimerB for x milliseconds
- move.b #0,$700(a1)
- move.b #$01,$e00(a1) ; TimerA continuous
- move.b #$41,$f00(a1) ; TimerB continuous,count A
- move.b #$82,$d00(a1) ; Enable TimerB interupt
- move.w #$2000,$dff09c ; Clear Interupt
- move.w #$e000,$dff09a ; Enable external interupt
- rts
-
- TimerStop
- move.w #$2000,$dff09a ; Disable external interupt
- move.l TimerVector(pc),$78 ; Restore old timer handler
- move.b #0,$bfde00 ; TimerA continuous
- move.b #0,$bfdf00 ; TimerB continuous
- move.w #$2000,$dff09c ; Clear Interupt
- move.w #$a000,$dff09a ; Enable external Interupt
- rts
-
- TimerVector: dc.l 0
-
- ;****************************************************************************
- ;****************************************************************************
-
- ;
- ; FORMAT
- ; 'SAND' , # of Sample.w, # Size of header.w,
- ; [ HEADER.l ] * # Size of header (including 'SAND' Sentry ID).
- ; [ SAMPLES ] (whatever memory they take)
- ;
-
- ; I am currently forcing only 16 samples, because of hardcoded memory space.
-
- SoundDecodeHunk
- cmp.l #'SAND',(a0)
- bne.s DecodeFail
- move.w 4(a0),d0 ; # = Samples
- cmp.w #999,d0
- bcc DecodeFail
- move.w 6(a0),d1 ; # = Size of Header
- cmp.w #999,d0
- bcc DecodeFail
-
- lsl.w #2,d1 ; * 4 (get longwords)
- move.l a0,a1
- addq.w #8,a0 ; A0 = Start of Offsets
- add.w d1,a1 ; A1 = Start of Data
-
- lea SoundList(pc),a2 ; A2 = DEST (List to Build)
-
- cmp.w #MAXSAM,d0 ; Allow 64 Samples Max, 1 Min
- bcs.s 1$
- moveq #MAXSAM,d0
- 1$ subq.w #1,d0
- bmi.s DecodeFail
-
- 2$ move.l a1,d1 ; START + OFFSET = RAW DATA ADDRESS
- move.l (a0)+,d2 ; Delta Start
- move.l (a0),d3 ; Delta End
- add.l d2,d1
- sub.l d2,d3
- lsr.w #1,d3
-
- move.l d1,(a2) ; Start
- move.w d3,4(a2) ; Length in words
- move.w #214,6(a2) ; Frequency
- move.w #64,8(a2) ; Volume
- move.w #2,10(a2) ; Priority
- move.w #0,12(a2) ; (pad)
- move.w #0,14(a2) ; (pad)
-
- add.w #16,a2 ; Next Sample
-
- dbf d0,2$
- moveq #1,d0
- rts
- DecodeFail
- moveq #0,d0
- rts
-
-
- ; The Go Anywhere Loader
- ; You might find the following useful, its a forget-about-memory-management
- ; file loading subroutine, you just pass it the name and it returns to you
- ; a pointer to the start. When exiting you call ReleaseFile (with the
- ; same pointer you got earlier) , which just frees up the memory that it used
- ; I finally decided that I needed a permanent utility like this...
- ;***************************************************************************
- ;***************************************************************************
-
- LVODelay equ -30-168
- LVODeviceProc equ -30-144
- LVOSeek equ -30-36
- LVOOutput equ -30-30
- LVOInput equ -30-24
- LVOOpen equ -30
- LVOWrite equ -30-18
- LVORead equ -30-12
- LVOClose equ -30-6
- LVOLock equ -$54
- LVOUnLock equ -$5A
- LVOExamine equ -$66
- LVOOpenLibrary equ -408
- AllocMem equ -30-168
- FreeMem equ -30-180
- AllocAbs equ -30-$cc
-
- ;***********************************************************************
- ;***********************************************************************
- ;
- ; GetFile, Load a file, return pointer to memory, this is a generic utility
- ; Passed
- ; A0 = File Name
- ; Returns
- ; A0 = Pointer to Start, (Save this Pointer for Close Down)
- ;
- LoadScore
- SoundGetHunk
- GetFile
- move.l a0,a3
- bsr GetDosBase ; A6 = Dos Base ***
- move.l a3,d1 ; D1 = File Name to Examine
- bsr.s FindFileSize ; D0 = File Size
- beq.s GetFailure
- addq.l #4,d0 ; +4 for internal header
- move.l d0,a2 ; A2 = Internal Size
- bsr AllocateMemory ; Pass D0=FileSize, return D0=Mem
- beq.s GetFailure
- move.l a3,d1 ; D1 = File Name to Load
- move.l d0,a3 ; A3 = Start
- move.l a2,(a3)+ ; A2 = Size (hide it for later freeing)
- bsr LoadFile
- beq ReleaseFile
- move.l a3,a0 ; Success
- moveq #1,d0
- GetFailure
- rts
-
- ;***********************************************************************
- ;
- ; ReleaseFile, generic utility to work in conjunction with GetFile
- ; Passed
- ; A1 = Pointer to GetFile Handle on file
- ReleaseScore
- SoundReleaseHunk
- ReleaseFile
- move.l -(a1),d0 ; D0 = Get Header, Size to Release
- bsr FreeMemory
- moveq #0,d0 ; (return as failure always)
- rts
- ;***********************************************************************
- AllocateMemory
- ; D0 = Amount
- move.l #2,d1 ; chip only
- move.l a6,-(a7)
- move.l 4,a6 ; Exec base
- jsr AllocMem(a6)
- move.l (a7)+,a6
- tst.l d0
- rts
- ;***********************************************************************
- FreeMemory
- ; A1 = Where, D0 = Amount
- move.l a6,-(a7)
- move.l 4,a6
- jsr FreeMem(a6)
- move.l (a7)+,a6
- rts
- ;***********************************************************************
- ;***********************************************************************
- ;
- ; Returns A6 = DosBase (I *know* that this will never fail)
- ;
- GetDosBase
- move.l 4,a6 ; Open DOS.LIBRARY
- lea DosName(pc),a1
- moveq #0,d0 ; (any rev #)
- jsr LVOOpenLibrary(a6)
- move.l d0,a6 ; A6 = DOS.LIBRARY
- rts
- ;***********************************************************************
- ;***********************************************************************
- ;
- ; LoadFile, a generic utility
- ; Passed
- ; D1=NAME, A3=DEST, A2=SIZE
- ; Returns
- ; success=1/fail=0
- ;
- LoadFile
- movem.l d1/a2/a3,-(a7)
- bsr GetDosBase
- movem.l (a7)+,d1/a2/a3
- ; D1 = Get FileName
- move.l #1005,d2 ; D2 = MODE_OLDFILE LOAD
- jsr LVOOpen(a6) ; Open the file
- move.l d0,d4 ; d4 is reserved as file ptr
- beq LoadFailure
- move.l a3,d2 ; D2 = Start
- move.l a2,d3 ; D3 = Size
- move.l d4,d1 ; D1 = File Pointer
- jsr LVORead(a6)
- move.l d4,d1 ; FP
- jsr LVOClose(a6) ; Close File
- moveq #1,d0 ; Success
- LoadFailure
- rts
- ;***********************************************************************
- ;***********************************************************************
- ; D1=NAME, A3=SOURCE, A2=SIZE
- SaveFile
- movem.l d1/a2/a3,-(a7)
- bsr GetDosBase
- movem.l (a7)+,d1/a2/a3
- move.l #1006,d2 ; D2 = MODE_NEWFILE SAVE
- jsr LVOOpen(a6) ; Open the file
- move.l d0,d4 ; d4 is reserved as file ptr
- beq LoadFailure
- move.l a3,d2 ; D2 = Start
- move.l a2,d3 ; D3 = Size
- move.l d4,d1 ; D1 = File Pointer
- jsr LVOWrite(a6)
- move.l d4,d1 ; FP
- jsr LVOClose(a6) ; Close File
- moveq #1,d0 ; Success
- rts
-
- ;***********************************************************************
- ;***********************************************************************
- ;
- ; Find the size of a file, a generic utility
- ; Passed
- ; D1 = Pointer to File Name (may have to be longword aligned)
- ; Returns
- ; D0 = Size of File or 0.
- ;
- ;LVOLock equ -$54
- ;LVOUnLock equ -$5A
- ;LVOExamine equ -$66
- ;LVOOpenLibrary equ -408
- ;
-
- FindFileSize
- ; D1 = File Name to Examine
- moveq #-2,d2
- jsr LVOLock(a6) ; D1=Name,D2=Mode
- move.l d0,d4
- beq.s Failure
- lea FileInfoBlock(pc),a2
-
- move.l a2,d0 ; manually force long word alignment
- btst #1,d0 ; (i'm not sure if this is necessary but...)
- beq.s 1$
- addq.w #2,a2
- 1$
- move.l a2,d2
- move.l d4,d1
- jsr LVOExamine(a6) ; D1=Lock,D2=FIB
- move.l d0,-(a7)
- move.l d4,d1
- jsr LVOUnLock(a6) ; D1=Lock
- move.l (a7)+,d0
- beq.s Failure
- move.l $007C(a2),d0 ; D0 = Size of File
- rts
- Failure moveq #0,d0 ; D0 = Failure
- rts
-
- DosName: dc.b 'dos.library',0
- even
- ;;; cnop 0,4 ; Longword Aligned
- ;;; ; (actually this may load wrong)
- FileInfoBlock dcb.b 268+2,0
-
- ;***********************************************************************
- ;***********************************************************************
-
-
- dumpmess
- movem.l d0-d7/a0-a6,-(a7)
- moveq #0,d3
- move.b (a0)+,d3 ; D3 = Length
- move.l a0,d2 ; D2 = What
- bsr GetDosBase
- ; jsr LVOOutput(a6) ; Get LVOOutput Handle
- ; move.l d0,d1 ; D1 = Output Handle
- move.l _stdout(pc),d1
- beq.s dumpnot
- jsr LVOWrite(a6) ; LVOWrite
- movem.l (a7)+,d0-d7/a0-a6
- dumpnot
- rts
-
- one dc.b 64-24,'Sequencer attempting to allocate memory',10,0
- even
- two dc.b 64-24,'Sequencer attempting to load music.hunk',10,0
- even
- three dc.b 66-24,'Sequencer attempting to decode music.hunk',10,0
- even
- four: dc.b 58-24,'Sequencer loaded user music.score',10,0
- even
- five: dc.b 43-24,'Sequencer is happy',10,0
- even
- six: dc.b 54-24,'Sequencer saved "music.score"',10,0
- even
- seven: dc.b 60-24,'Sequencer looking for "music.score"',10,0
- end
-
- ; end of tetrissound.s
-